use std::io::{fs, USER_RWX, File};
use std::str;
use std::sync::Mutex;
-use std::sync::mpsc::Sender;
use core::{Package, Target, PackageId, PackageSet};
use util::{CargoResult, human, Human};
pub metadata: Vec<(String, String)>,
}
+pub type BuildMap = HashMap<(PackageId, Kind), BuildOutput>;
+
pub struct BuildState {
- pub outputs: Mutex<HashMap<(PackageId, Kind), BuildOutput>>,
+ pub outputs: Mutex<BuildMap>,
}
/// Prepares a `Work` that executes the target as a custom build script.
let id = pkg.get_package_id().clone();
let all = (id.clone(), pkg_name.clone(), build_state.clone(),
build_output.clone());
+ let plugin_deps = super::crawl_build_deps(cx, pkg, target, Kind::Host);
try!(fs::mkdir_recursive(&cx.layout(pkg, Kind::Target).build(pkg), USER_RWX));
try!(fs::mkdir_recursive(&cx.layout(pkg, Kind::Host).build(pkg), USER_RWX));
//
// Note that this has to do some extra work just before running the command
// to determine extra environment variables and such.
- let work = move |: desc_tx: Sender<String>| -> CargoResult<()> {
+ let work = Work::new(move |desc_tx| {
// Make sure that OUT_DIR exists.
//
// If we have an old build directory, then just move it into place,
}
// For all our native lib dependencies, pick up their metadata to pass
- // along to this custom build command.
+ // along to this custom build command. We're also careful to augment our
+ // dynamic library search path in case the build script depended on any
+ // native dynamic libraries.
let mut p = p;
{
let build_state = build_state.outputs.lock().unwrap();
Some(value.as_slice()));
}
}
+ p = try!(super::add_plugin_deps(p, &*build_state, plugin_deps));
}
// And now finally, run the build command itself!
}));
Ok(())
- };
+ });
// Now that we've prepared our work-to-do, we need to prepare the fresh work
// itself to run when we actually end up just discarding what we calculated
let (freshness, dirty, fresh) =
try!(fingerprint::prepare_build_cmd(cx, pkg, kind, Some(target)));
let dirty = Work::new(move |tx| {
- try!(work(tx.clone()));
+ try!(work.call((tx.clone())));
dirty.call(tx)
});
let fresh = Work::new(move |tx| {
use std::collections::{HashSet, HashMap};
use std::dynamic_lib::DynamicLibrary;
+use std::ffi::CString;
use std::io::fs::{self, PathExtensions};
+use std::os;
use std::path;
use std::sync::Arc;
pub use self::context::Platform;
pub use self::engine::{CommandPrototype, CommandType, ExecEngine, ProcessEngine};
pub use self::layout::{Layout, LayoutProxy};
-pub use self::custom_build::BuildOutput;
+pub use self::custom_build::{BuildOutput, BuildMap};
mod context;
mod compilation;
let crate_types = target.rustc_crate_types();
let rustcs = try!(prepare_rustc(package, target, crate_types, cx, req));
- rustcs.into_iter().map(|(rustc, kind)| {
+ let plugin_deps = crawl_build_deps(cx, package, target, Kind::Host);
+
+ return rustcs.into_iter().map(|(rustc, kind)| {
let name = package.get_name().to_string();
let is_path_source = package.get_package_id().get_source_id().is_path();
let show_warnings = package.get_package_id() == cx.resolve.root() ||
// Prepare the native lib state (extra -L and -l flags)
let build_state = cx.build_state.clone();
- let mut native_lib_deps = HashSet::new();
let current_id = package.get_package_id().clone();
-
+ let plugin_deps = plugin_deps.clone();
+ let mut native_lib_deps = crawl_build_deps(cx, package, target, kind);
if package.has_custom_build() && !target.get_profile().is_custom_build() {
- native_lib_deps.insert(current_id.clone());
+ native_lib_deps.insert(0, current_id.clone());
}
- // Visit dependencies transitively to figure out what our native
- // dependencies are (for -L and -l flags).
- //
- // Be sure to only look at targets of the same Kind, however, as we
- // don't want to include native libs of plugins for targets for example.
- fn visit<'a>(cx: &'a Context, pkg: &'a Package, target: &Target,
- kind: Kind,
- visiting: &mut HashSet<&'a PackageId>,
- libs: &mut HashSet<PackageId>) {
- for &(pkg, target) in cx.dep_targets(pkg, target).iter() {
- let req = cx.get_requirement(pkg, target);
- if !req.includes(kind) { continue }
- if !visiting.insert(pkg.get_package_id()) { continue }
-
- if pkg.has_custom_build() {
- libs.insert(pkg.get_package_id().clone());
- }
- visit(cx, pkg, target, kind, visiting, libs);
- visiting.remove(&pkg.get_package_id());
- }
- }
- visit(cx, package, target, kind,
- &mut HashSet::new(), &mut native_lib_deps);
- let mut native_lib_deps = native_lib_deps.into_iter().collect::<Vec<_>>();
- native_lib_deps.sort();
// If we are a binary and the package also contains a library, then we
// don't pass the `-l` flags.
let mut rustc = rustc;
// Only at runtime have we discovered what the extra -L and -l
- // arguments are for native libraries, so we process those here.
- {
- let build_state = build_state.outputs.lock().unwrap();
- for id in native_lib_deps.into_iter() {
- let output = &build_state[(id.clone(), kind)];
- for path in output.library_paths.iter() {
- rustc = rustc.arg("-L").arg(path);
- }
- if pass_l_flag && id == current_id {
- for name in output.library_links.iter() {
- rustc = rustc.arg("-l").arg(name.as_slice());
- }
- }
- }
- }
+ // arguments are for native libraries, so we process those here. We
+ // also need to be sure to add any -L paths for our plugins to the
+ // dynamic library load path as a plugin's dynamic library may be
+ // located somewhere in there.
+ let build_state = build_state.outputs.lock().unwrap();
+ rustc = add_native_deps(rustc, &*build_state, native_lib_deps,
+ kind, pass_l_flag, ¤t_id);
+ rustc = try!(add_plugin_deps(rustc, &*build_state, plugin_deps));
+ drop(build_state);
// FIXME(rust-lang/rust#18913): we probably shouldn't have to do
// this manually
Ok(())
}), kind))
- }).collect()
+ }).collect();
+
+ // Add all relevant -L and -l flags from dependencies (now calculated and
+ // present in `state`) to the command provided
+ fn add_native_deps(mut rustc: CommandPrototype,
+ build_state: &BuildMap,
+ native_lib_deps: Vec<PackageId>,
+ kind: Kind,
+ pass_l_flag: bool,
+ current_id: &PackageId) -> CommandPrototype {
+ for id in native_lib_deps.into_iter() {
+ let output = &build_state[(id.clone(), kind)];
+ for path in output.library_paths.iter() {
+ rustc = rustc.arg("-L").arg(path);
+ }
+ if pass_l_flag && id == *current_id {
+ for name in output.library_links.iter() {
+ rustc = rustc.arg("-l").arg(name.as_slice());
+ }
+ }
+ }
+ return rustc;
+ }
+}
+
+fn crawl_build_deps<'a>(cx: &'a Context, pkg: &'a Package,
+ target: &Target, kind: Kind) -> Vec<PackageId> {
+ let mut deps = HashSet::new();
+ visit(cx, pkg, target, kind, &mut HashSet::new(), &mut deps);
+ let mut ret: Vec<_> = deps.into_iter().collect();
+ ret.sort();
+ return ret;
+
+ fn visit<'a>(cx: &'a Context, pkg: &'a Package, target: &Target,
+ kind: Kind,
+ visiting: &mut HashSet<&'a PackageId>,
+ libs: &mut HashSet<PackageId>) {
+ for &(pkg, target) in cx.dep_targets(pkg, target).iter() {
+ let req = cx.get_requirement(pkg, target);
+ if !req.includes(kind) { continue }
+ if !visiting.insert(pkg.get_package_id()) { continue }
+
+ if pkg.has_custom_build() {
+ libs.insert(pkg.get_package_id().clone());
+ }
+ visit(cx, pkg, target, kind, visiting, libs);
+ visiting.remove(&pkg.get_package_id());
+ }
+ }
+}
+
+// For all plugin dependencies, add their -L paths (now calculated and
+// present in `state`) to the dynamic library load path for the command to
+// execute.
+fn add_plugin_deps(rustc: CommandPrototype,
+ build_state: &BuildMap,
+ plugin_deps: Vec<PackageId>)
+ -> CargoResult<CommandPrototype> {
+ let var = DynamicLibrary::envvar();
+ let search_path = rustc.get_env(var)
+ .unwrap_or(CString::from_slice(b""));
+ let mut search_path = os::split_paths(search_path);
+ for id in plugin_deps.into_iter() {
+ let output = &build_state[(id, Kind::Host)];
+ for path in output.library_paths.iter() {
+ search_path.push(path.clone());
+ }
+ }
+ let search_path = try!(join_paths(&search_path[], var));
+ Ok(rustc.env(var, Some(search_path)))
}
fn prepare_rustc(package: &Package, target: &Target, crate_types: Vec<&str>,
-use std::io::File;
+use std::io::{File, fs};
+use std::os;
use support::{project, execs, cargo_dir};
use support::{COMPILING, RUNNING, DOCTEST};
assert_that(p.cargo_process("test"), execs().with_status(0));
});
+
+test!(build_script_with_dynamic_native_dependency {
+ let build = project("builder")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "builder"
+ version = "0.0.1"
+ authors = []
+
+ [lib]
+ name = "builder"
+ crate-type = ["dylib"]
+ "#)
+ .file("src/lib.rs", r#"
+ #[no_mangle]
+ pub extern fn foo() {}
+ "#);
+ assert_that(build.cargo_process("build"),
+ execs().with_status(0).with_stderr(""));
+ let src = build.root().join("target");
+ let lib = fs::readdir(&src).unwrap().into_iter().find(|lib| {
+ let lib = lib.filename_str().unwrap();
+ lib.starts_with(os::consts::DLL_PREFIX) &&
+ lib.ends_with(os::consts::DLL_SUFFIX)
+ }).unwrap();
+ let libname = lib.filename_str().unwrap();
+ let libname = libname.slice(os::consts::DLL_PREFIX.len(),
+ libname.len() - os::consts::DLL_SUFFIX.len());
+
+ let foo = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ build = "build.rs"
+
+ [build-dependencies.bar]
+ path = "bar"
+ "#)
+ .file("build.rs", r#"
+ extern crate bar;
+ fn main() { bar::bar() }
+ "#)
+ .file("src/lib.rs", "")
+ .file("bar/Cargo.toml", r#"
+ [package]
+ name = "bar"
+ version = "0.0.1"
+ authors = []
+ build = "build.rs"
+ "#)
+ .file("bar/build.rs", r#"
+ use std::os;
+
+ fn main() {
+ let src = Path::new(os::getenv("SRC").unwrap());
+ println!("cargo:rustc-flags=-L {}", src.dir_path().display());
+ }
+ "#)
+ .file("bar/src/lib.rs", format!(r#"
+ pub fn bar() {{
+ #[link(name = "{}")]
+ extern {{ fn foo(); }}
+ unsafe {{ foo() }}
+ }}
+ "#, libname));
+
+ assert_that(foo.cargo_process("build").env("SRC", Some(lib.as_vec())),
+ execs().with_status(0));
+});